
/**		
 * 
 *	uk.co.leanmean.net.loading.QueueLoaderItem
 *	
 *	@version 1.00 | Oct 12, 2009
 *	@author Justin Windle
 *  
 **/
 
package uk.co.lmfm.net.loading 
{
	import uk.co.lmfm.net.loading.events.QueueLoaderEvent;

	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.system.LoaderContext;
	import flash.utils.getQualifiedClassName;

	/**
	 * QueueLoaderItem
	 */
	public class QueueLoaderItem extends EventDispatcher 
	{
		
		//	----------------------------------------------------------------
		//	PUBLIC MEMBERS
		//	----------------------------------------------------------------
		
		/**
		 * An empty object to hold any extra information in
		 */
		
		public var extra: *;
		 
		//	----------------------------------------------------------------
		//	PRIVATE MEMBERS
		//	----------------------------------------------------------------

		private var _id : *;
		private var _url : String;
		private var _data : *;
		private var _fileType : String;		private var _fileExtension : String;
		private var _cache : Boolean;
		private var _priority : Number;
		private var _timeStamp : Boolean;
		private var _urlLoader : URLLoader;
		private var _bytesLoaded : int;
		private var _bytesTotal : int;
		private var _loader : Loader;

		//	----------------------------------------------------------------
		//	CONSTRUCTOR
		//	----------------------------------------------------------------
		
		/**
		 * Creates a new QueueLoaderItem instance
		 * 
		 * @param url The path of the item to load
		 * @param fileType The type of file being loaded. Defaults to 'auto', which 
		 * will attempt to determine the file type from the url
		 * @see uk.co.leanmean.net.loading.QueueLoader#FILETYPE_AUTO
		 * @see uk.co.leanmean.net.loading.QueueLoader#FILETYPE_IMAGE
		 * @see uk.co.leanmean.net.loading.QueueLoader#FILETYPE_VIDEO
		 * @see uk.co.leanmean.net.loading.QueueLoader#FILETYPE_TEXT
		 * @see uk.co.leanmean.net.loading.QueueLoader#FILETYPE_SWF
		 * @see uk.co.leanmean.net.loading.QueueLoader#FILETYPE_XML
		 * @param id An optional ID to use for retrieving the item later. If null, 
		 * the url of the item will be used as the ID. Any object, not only Strings, 
		 * can be used to identify this item.
		 * @param priority The priority of this item (leave unchanged to load in the 
		 * usual order). You can also change an items priority at any time using the 
		 * priority accessor for the item itself.
		 * @param cache Whether or not this item should be cached in memory for 
		 * fast retrieval if the item is loaded again (saves on URLRequests).
		 * @param timestamp Whether to append a timestamp to the url in order to 
		 * prevent browser caching (useful for files that will change on the server)
		 */

		public function QueueLoaderItem(__url : String, __fileType : String, __id : *, __priority : Number, __cache : Boolean, __timestamp : Boolean)
		{
			_id = __id;
			_url = __url;
			_cache = __cache;
			_fileType = __fileType;
			_priority = __priority;
			_timeStamp = __timestamp;
			_fileExtension = __url.match(/\.([^\.]+)$/)[1].replace(/\?.*/, '').toLowerCase();
			
			if(_fileType == QueueLoader.FILETYPE_AUTO)
			{
				switch(_fileExtension)
				{
					case 'txt' : case 'rtf' : case 'htm' : case 'html' : case 'php' : case 'asp' : case 'aspx' : default :
						
						_fileType = QueueLoader.FILETYPE_TEXT;
						
						break;
						
					case 'jpg' : case 'jpeg' : case 'png' : case 'gif' : case 'bmp' : 
						
						_fileType = QueueLoader.FILETYPE_IMAGE;
						
						break;
						
					case 'mpg' : case 'mpeg' : case 'mp4' : case 'mov' : 
						
						_fileType = QueueLoader.FILETYPE_VIDEO;
						
						break;
						
					case 'swf' :
						
						_fileType = QueueLoader.FILETYPE_SWF;
						
						break;
						
					case 'xml' :
						
						_fileType = QueueLoader.FILETYPE_XML;
						
						break;
				}
			}
			
			_bytesLoaded = 0;
			_bytesTotal = 0;
		}

		//	----------------------------------------------------------------
		//	PUBLIC METHODS
		//	----------------------------------------------------------------
		
		/**
		 * Starts loading this items content
		 */

		public function start() : void
		{
			var path : String = _url + (_timeStamp ? "?t=" + new Date().getTime() : '');
			
			switch(_fileType)
			{
				case QueueLoader.FILETYPE_IMAGE : 
				case QueueLoader.FILETYPE_VIDEO :
				case QueueLoader.FILETYPE_SWF : 
					
					var context : LoaderContext = new LoaderContext();
					context.checkPolicyFile = true;
					
					_loader = new Loader();
					_loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
					_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, onLoadProgress);
					_loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);					_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
					_loader.load(new URLRequest(path), context);
					
					break;
					
				case QueueLoader.FILETYPE_TEXT : 				case QueueLoader.FILETYPE_XML : 
				
					_urlLoader = new URLLoader();
					_urlLoader.dataFormat = URLLoaderDataFormat.TEXT;
					_urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
					_urlLoader.addEventListener(ProgressEvent.PROGRESS, onLoadProgress);
					_urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
					_urlLoader.addEventListener(Event.COMPLETE, onLoadComplete);
					_urlLoader.load(new URLRequest(path));
				
					break;
					
				case QueueLoader.FILETYPE_BINARY :
				
					_urlLoader = new URLLoader();
					_urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
					_urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
					_urlLoader.addEventListener(ProgressEvent.PROGRESS, onLoadProgress);
					_urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onLoadError);
					_urlLoader.addEventListener(Event.COMPLETE, onLoadComplete);
					_urlLoader.load(new URLRequest(path));
				
				break; 
			}
			
			var qEvent : QueueLoaderEvent = new QueueLoaderEvent(QueueLoaderEvent.ITEM_STARTED);
			qEvent.item = this;
			
			dispatchEvent(qEvent);
		}

		/**
		 * Stops loading this items content
		 */

		public function stop() : void
		{
			var qEvent : QueueLoaderEvent = new QueueLoaderEvent(QueueLoaderEvent.ITEM_STOPPED);
			qEvent.item = this;
			
			dispatchEvent(qEvent);
		}

		/**
		 * Stops the loading process (if necessary) for this item and attempts 
		 * to unload the content as well as destroying all references to the 
		 * loader so that it can be cleared from memory.
		 */

		public function destroy() : void
		{
			removeListeners();
		}

		/**
		 * Returns a string representation of this object
		 */

		override public function toString() : String
		{
			return "id: " + _id + ", url: " + _url;
		}

		//	----------------------------------------------------------------
		//	PRIVATE METHODS
		//	----------------------------------------------------------------

		private function removeListeners() : void
		{
			var target : EventDispatcher = _loader ? _loader.contentLoaderInfo : _urlLoader ? _urlLoader : null;
			
			if(!target) return;
			
			target.removeEventListener(IOErrorEvent.IO_ERROR, onLoadError);
			target.removeEventListener(ProgressEvent.PROGRESS, onLoadProgress);
			target.removeEventListener(Event.COMPLETE, onLoadComplete);
			
			if(_urlLoader) try { _urlLoader.close(); } catch(error : Error) { ; } 
			if(_loader) _loader.unload();
			
			_data = null;
		}

		//	----------------------------------------------------------------
		//	EVENT HANDLERS
		//	----------------------------------------------------------------
		
		private function onSecurityError(event : SecurityErrorEvent):void
		{
			var qEvent : QueueLoaderEvent = new QueueLoaderEvent(QueueLoaderEvent.ITEM_ERROR);
			qEvent.progress = progress;
			qEvent.text = event.text;
			qEvent.item = this;
			
			dispatchEvent(qEvent);
		}

		private function onLoadError(event : IOErrorEvent) : void
		{
			var qEvent : QueueLoaderEvent = new QueueLoaderEvent(QueueLoaderEvent.ITEM_ERROR);
			qEvent.progress = progress;
			qEvent.text = event.text;
			qEvent.item = this;
			
			dispatchEvent(qEvent);
		}

		private function onLoadProgress(event : ProgressEvent) : void
		{
			_bytesTotal = event.bytesTotal;
			_bytesLoaded = event.bytesLoaded;
			
			var qEvent : QueueLoaderEvent = new QueueLoaderEvent(QueueLoaderEvent.ITEM_PROGRESS);
			qEvent.progress = _bytesLoaded / _bytesTotal;
			qEvent.item = this;
			
			dispatchEvent(qEvent);
		}

		private function onLoadComplete(event : Event) : void
		{
			switch(_fileType)
			{
				case QueueLoader.FILETYPE_IMAGE : 
				case QueueLoader.FILETYPE_VIDEO :
				case QueueLoader.FILETYPE_SWF : 
				
					_data = _loader.content;
					
					break;
					
				case QueueLoader.FILETYPE_BINARY :
				case QueueLoader.FILETYPE_TEXT : 
				
					_data = _urlLoader.data;
					
					break;
					
				case QueueLoader.FILETYPE_XML : 
				
					try { _data = new XML(_urlLoader.data); }
					catch( error:Error ) { ; }
				
					break;
			}
			
			var qEvent : QueueLoaderEvent = new QueueLoaderEvent(QueueLoaderEvent.ITEM_COMPLETE);
			qEvent.progress = 1.0;
			qEvent.item = this;
			
			dispatchEvent(qEvent);
		}

		//	----------------------------------------------------------------
		//	PUBLIC ACCESSORS
		//	----------------------------------------------------------------
		
		/**
		 * The priority of this item. A higher value will bump the item higher 
		 * in the QueueLoaders queue.
		 */

		public function get priority() : Number
		{
			return _priority;
		}

		public function set priority(value : Number) : void
		{
			_priority = value;
			
			var qEvent : QueueLoaderEvent = new QueueLoaderEvent(QueueLoaderEvent.ITEM_PRIORISED);
			qEvent.item = this;
			
			dispatchEvent(qEvent);
		}

		/**
		 * Whether or not this items content should be cached
		 */

		public function get cache() : Boolean
		{
			return _cache;
		}		

		/**
		 * The total number of bytes in this items content
		 */

		public function get bytesTotal() : int
		{
			return _bytesTotal;
		}

		/**
		 * The number of bytes that have currently been loaded
		 */

		public function get bytesLoaded() : int
		{
			return _bytesLoaded;
		}

		/**
		 * The current progress of the load (0 -> 1)
		 */

		public function get progress() : Number
		{
			return _bytesLoaded / _bytesTotal || 0.0;
		}		

		/**
		 * The ID used to identify this item within the loading queue
		 */

		public function get id() : *
		{
			return _id;
		}

		/**
		 * The URL being loaded by this item
		 */

		public function get url() : String
		{
			return _url;
		}

		public function set url(value : String) : void
		{
			_url = value;
		}

		/**
		 * The data loaded by this item (e.g. Bitmap or XML)
		 */

		public function get data() : *
		{
			return _data;
		}

		/**
		 * The file type of the loaded data. This is the broad file type, 
		 * used inside the loader queue to which this item belongs. If you 
		 * wish to find the file extension (jpg, bmp, xml etc...) use the 
		 * fileExtension property.
		 * @see #FILETYPE_IMAGE
		 * @see #FILETYPE_VIDEO
		 * @see #FILETYPE_TEXT
		 * @see #FILETYPE_SWF
		 * @see #FILETYPE_XML
		 */

		public function get fileType() : String
		{
			return _fileType;
		}

		/**
		 * The file extension of the file being loaded. This is automatically 
		 * determined by parsing the url and therfor may not be accurate for 
		 * dynamic content or urls where the extension of the file is not clear 
		 * or is misleading (for example http://mysite.com/getData.php?page='home')
		 */

		public function get fileExtension() : String
		{
			return _fileExtension;
		}

		/**
		 * The class type of the data loaded after the load is complete 
		 * and the content has been wrapped inside an ActionScript object. 
		 * For example, if the loaded data is an image, asDataType will be 
		 * flash.display::Bitmap
		 */	

		public function get asDataType() : String
		{
			return _data ? getQualifiedClassName(_data) : 'null';
		}
	}
}
